/*
	Работа с локальной сетью + протокол P32
*/
#include "defines.h"

//Наши сетевые параметры
U8 * mac = (U8 *)MAC_ADDRESS;

//Приемный буфер
U8 tcp_recv_buffer[100];
U8 tcp_recv_bufsize=0;

//Буфер отправки
U8 tcp_send_buffer[TCP_SEND_BUFSIZE];
U16 tcp_send_bufsize=0;

//Буферы конфигурационных сокетов
U8 tcpServer_recvbuffer[2*1024];
U16 tcpServer_recvbufcount=0;

U8 udpServer_recvbuffer[2*1024];
U16 udpServer_recvbufcount=0;


//Сколько данных отправили??
U16 tcp_send_done=0;

//Таймеры
U64 tcpTime=0;
U64 tcpExchangeTime=0x0000FFFF;
U64 tcpSendTime=0;
U64 tcpRecvTime=0;

//Наши сокеты
U8 tcpSock;
U8 tcpServer;
U8 udpServer;

//UDP или TCP
U8 udpActive;

//Состояние сеанса TCP соединения
TcpStateT tcpState = TCP_IDLE;

//IP адрес сервера
U8 serverIP[4];
U16 serverPort;

//Регистрация терминала на сервере
U8 regOk=0;
U8 currentCmd;

//Запрос к DHCP
U8 needDHCP=0;
U64 dhcpTime;

U8 netParamsOK=0;
//Используем UDP?
U8 udpActive;
extern U8 own_hw_adr[];
extern LOCALM localm[];

//А кто к нам обратился по UDP
U8 udpClient_ip[4];
U16 udpClient_port;

/* Local Machine Settings      */
#define MY_IP localm[NETIF_ETH].IpAdr


/*
	Настройка ethernet
*/
void ethernet_init(void)
{
	//Копируем MAС адрес
	memcpy(own_hw_adr,mac,6);
	//Настройка Ethernet
	init_TcpNet();
	if (!needDHCP)
		dhcp_disable();
	//Инициализация необходимых сокетов
	sockets_init();
	
}

/*
	Создаем Tcp сокет для подключения к сокету
*/
void sockets_init(void)
{
	//Главный TCP сокет
	tcpSock = tcp_get_socket (TCP_TYPE_CLIENT, 0, 30, tcp_callback);
	if (!tcpSock) 
	{
		printf("Error create tcp socket\r\n");
	}
	
	//TCP server
	tcpServer = tcp_get_socket (TCP_TYPE_SERVER|TCP_TYPE_KEEP_ALIVE, 0, 30, tcpServer_callback);
	tcp_listen(tcpServer,SERVER_CONFIG_TCP_PORT);
	
	//UDP Server
	udpServer = udp_get_socket(0,UDP_OPT_SEND_CS | UDP_OPT_CHK_CS, udpServer_callback);
	udp_open(udpServer,SERVER_CONFIG_UDP_PORT);
}

/*
	Отправляем данные через TCP
*/
U8 tcp_send_data(U8 * data, U16 size, U8 sock)
{
	U8 *sendbuf;
  U16 maxlen;	
	
	if (tcp_check_send (sock))
	{
		//The socket is ready to send the data. 
		maxlen = tcp_max_dsize (sock);
		sendbuf = tcp_get_buf (maxlen);
		memcpy(sendbuf,data,size);
		tcp_send (sock, sendbuf, size);
		return 1;
	}
	else 
		return 0;
	
}

/*
	TCP/IP цикл
*/
void tcpip_prc(void)
{
	//Тикаем каждые 100мс
	if (SystemTick-tcpTime>=100)
	{
		tcpTime = SystemTick;
		timer_tick();
	}
	//Тут обрабатываем tcp\ip пакеты
	main_TcpNet ();
	//Автомат приема/ответа данных - только когда получили IP адрес
	if(netParamsOK)
	{
		tcp_link_prc();
		//Приняли данные по TCP?
		if (tcpServer_recvbufcount)
		{
			udpActive=0;
			//Парсим данные
			link_parse_packet(tcpServer_recvbuffer,tcpServer_recvbufcount,tcpServer);
			//Обнуляем счетчики
			tcpServer_recvbufcount=0;
		
		}
		//А может по UDP есть?
		if(udpServer_recvbufcount)
		{
			udpActive=1;
			link_parse_packet(udpServer_recvbuffer,udpServer_recvbufcount,udpServer);
			udpServer_recvbufcount=0;
		}
	}
	
	//Запрос адреса
	if (needDHCP)
		dhcp_check ();
	
}

/*
	Связь с сервером
*/
void tcp_link_prc()
{
	U16 try_send_size=0;
	switch(tcpState)
	{
		//-------------------------------------------
		//Ничего не делаем, ждем когда надо начинать передавать данные
		case TCP_IDLE:
		{
			//Мы зарегестрированы и пора передавать данные - устанавливать соединение не надо 
			if ((regOk)&&(SystemTick - tcpExchangeTime>=mainConfig.mainInterval))
			{
					currentCmd = P32_CMD_ASK;
					tcpState = TCP_BEFORE_SEND;
					tcpExchangeTime= SystemTick;	
			}
			
			//Мы не зарегестрированы и пора сделать попытку установить соединение
			if ((!regOk)&&(SystemTick-tcpExchangeTime>=REG_INTERVAL))
			{
					tcpState = TCP_CONNECT;
					tcpExchangeTime= SystemTick;	
			}
			break;
		}
		//---------------------------------------------
		//Устанавливаем соединение с сервером
		case TCP_CONNECT:
		{
			//Пора устанавливать соединение!
			printf("Try connect to server %d.%d.%d.%d\r\n", serverIP[0],serverIP[1],serverIP[2],serverIP[3]);
			tcpExchangeTime = SystemTick;			//Запоминаем время когда последний раз устанавливали соединение
			tcpState = TCP_WAIT_CONNECT;			//Следующее состояние
										
			if (tcp_get_state(tcpSock)!=TCP_STATE_CLOSED)
				tcp_abort(tcpSock);
			
			printf("Sock state: %d\r\n", tcp_get_state(tcpSock));
			//Пробуем подключиться
			tcp_connect (tcpSock, serverIP, serverPort, LOCAL_TCP_PORT);
						
			break;
		}
		
		//---------------------------------------------
		//Ждем установки соединения с сервером!!
		case TCP_WAIT_CONNECT:
		{
			//Подключились!!!
			if (tcp_get_state(tcpSock) == TCP_STATE_CONNECT)
			{
				printf("Connect OK\r\n");
				//Раз уж установили соединение, то надо зарегестрироваться на сервере
				
				#if 0
					journalTicks =0;
					currentTicks = rtc_get_time_in_ticks();
					currentCmd = P32_CMD_DATA;
				#else
					currentCmd = P32_CMD_REG;
				#endif
				
				tcpState = TCP_BEFORE_SEND;
			}
			else 
			{
				//Таймаут!! - не можем подключиться к серверу
				if (SystemTick-tcpExchangeTime>=CONNECT_TIMEOUT)
				{
					printf("Connect timeout\r\n");
					tcpState = TCP_CLOSE;
				}
			}
			break;
		}
		//--------------------------------------------------
		//Подготавливаем  данные к отправке
		case TCP_BEFORE_SEND:
		{
			tcp_before_send();
			//Запоминаем время для таймаута
			tcpSendTime  = SystemTick;
			//Обнуляем счетчик переданных байт
			tcp_send_done=0;
			//Следующее состояние
			tcpState = TCP_SEND;
			break;
		}
		//---------------------------------------------------
		//Собственно отправка данных
		case TCP_SEND:
		{
			//За раз можно передать только максимум TCP_MTU байт
			if (tcp_send_bufsize-tcp_send_done>TCP_MTU)
				try_send_size = TCP_MTU;
			else 
				try_send_size = tcp_send_bufsize-tcp_send_done;
			
			printf("Try to send %d bytes. Sock state: %d...",try_send_size,tcp_get_state(tcpSock));
			//Пробуем отправить даные
			if (!tcp_send_data(tcp_send_buffer+tcp_send_done, try_send_size,tcpSock))
			{
				//Таймаут - не можем отправить данные
				if (SystemTick-tcpSendTime>=SEND_TIMEOUT)
				{
					printf("Send data timeout\r\n");
					tcpState =TCP_CLOSE;
					break;
				}
			}
			//Отправили данные!!!
			else 
			{
				printf("OK\r\n");
				tcp_send_done += try_send_size;
			}
			
			//Проверяем - может все (Отправили один пакет)???
			if (tcp_send_done>=tcp_send_bufsize)
			{
				//В зависимости от команды переходим в другое состояние
				if ((currentCmd==P32_CMD_REG)||(currentCmd == P32_CMD_ASK))
				{
					tcpRecvTime = SystemTick;
					tcp_recv_bufsize=0;
					tcpState = TCP_WAIT_ANSWER;
				}
				//Отправили пакет с данными, надо проверить - есть ли еще данные.
				else if (currentCmd ==P32_CMD_DATA)
				{
					//Если было отправлено ровно MAX_COUNT_PER_PACKET пакетов, то пошлем еще один ACK
					if (JournalRecordsCount==MAX_COUNT_PER_PACKET)
					{
							tcpState = TCP_BEFORE_SEND;
							currentCmd = P32_CMD_ASK;
					}
					else
					{
							tcpState =TCP_IDLE;
					}
					
					//tcpState = TCP_BEFORE_SEND;
					//tcpState = TCP_BEFORE_SEND;
					//currentCmd = P32_CMD_ASK;
				}
			}
			break;
		}
		//--------------------------------------------------------------
		//Ждем ответа от сервера
		case TCP_WAIT_ANSWER:
		{
			//Приняли данные
			if (tcp_recv_bufsize)
			{
				printf("Recieved %d bytes\r\n", tcp_recv_bufsize);
				tcpState = tcp_parse_data();
			}
			//Нет данных
			else 
			{
				//Таймаут
				if(SystemTick-tcpRecvTime>=RECV_TIMEOUT)
				{
					printf("Recv data timeout\r\n");
					tcpState = TCP_CLOSE;
				}
			}
			break;
		}
		//--------------------------------------------------------------
		//Закрываем соединение и возвращаемся в исходную позицию
		case TCP_CLOSE:
		{
			
			//Закрываем соединение
			tcpState = TCP_IDLE;
			tcp_close(tcpSock);
			//Надо снова регистрироваться на сервере
			regOk = 0;
			printf("Close session. Need to reg\r\n");
			break;
		}
	}
}

/*
	Подготавливаем данные к передаче
*/
U8 tcp_before_send(void)
{
	switch (currentCmd)
	{
		//подготавливаем пакет REG
		case P32_CMD_REG:
		{
			printf("Send REG cmd\r\n");
			tcp_send_buffer[0] = P32_VER;
			tcp_send_buffer[1] = P32_BUILD;
			tcp_send_buffer[2] = P32_CMD_REG;
			memcpy(tcp_send_buffer+3,mac,6);
			memcpy(tcp_send_buffer+9,localm[NETIF_ETH].IpAdr,IP_ADRLEN);
			tcp_send_bufsize=13;
			break;
		}
		//Подготавливаем пакет ASK
		case P32_CMD_ASK:
		{
			printf("Send ASK cmd\r\n");
			tcp_send_buffer[0] = P32_VER;
			tcp_send_buffer[1] = P32_BUILD;
			tcp_send_buffer[2] = P32_CMD_ASK;
			memcpy(tcp_send_buffer+3,mac,6);
			memcpy(tcp_send_buffer+9,localm[NETIF_ETH].IpAdr,IP_ADRLEN);
			tcp_send_bufsize=13;
			break;
		}
		//Подготавливаем данные для отправки - тут отдельная песня
		case P32_CMD_DATA:
		{
			
			int i;	
			P32T hP32;
			P32T nP32;
			
			printf("Send Data cmd\r\n");
			//очищаем
			memset(tcp_send_buffer,0x00,sizeof(tcp_send_buffer));
			//Считываем записи
			journal_get_records();
			
			//Свопим количество записей
			tcp_send_buffer[0] = (U8)((JournalRecordsCount>>8)&0xFF);
			tcp_send_buffer[1] = (U8)(JournalRecordsCount&0xFF);
			//Тут свопим данные
			for (i=0;i<JournalRecordsCount;i++)
			{
				hP32 = JournalRecords[i];
				memset(&nP32,0x00,sizeof(nP32));
				h_to_n_P32(&hP32,&nP32);
				memcpy(tcp_send_buffer+2+i*sizeof(P32T),&nP32,sizeof(P32T));
			}
			tcp_send_bufsize = JournalRecordsCount*sizeof(P32T)+2;
		
			break;
		}
	}
	return 1;
}

/*
	Парсим входные данные
*/
TcpStateT tcp_parse_data()
{
	switch (currentCmd)
	{
		//Принимаем время в тиках - надо синхронизировать наши часы
		case P32_CMD_REG:
		{
			if (tcp_recv_bufsize==8)
			{
				//Переводим из сетевого формата в формат хоста
				U64 nTime = *((U64 *)tcp_recv_buffer);
				U64 hTime;
				swap((U8 *)&nTime,(U8 *)&hTime,sizeof(hTime));
				
				//Если установили время, то все ОК
				if (rtc_set_time_in_ticks(hTime))
				{
					
					DateTimeT Time = rtc_get_time();
					printf("Date and Time: %02d.%02d.%04d %02d:%02d:%02d\r\n",Time.Mday,Time.Mon,Time.Year,Time.Hour,Time.Min, Time.Sec);
					regOk=1;
					printf("Registration OK\r\n");
					
					currentCmd = P32_CMD_ASK;
					return TCP_BEFORE_SEND;
				}
				else
					printf("Error recieved time\r\n");
			}
			break;
		}
		case P32_CMD_ASK:
		{
			//Считываем время в тиках, с какого момента надо передавать данные
			if (tcp_recv_bufsize==8)
			{
				
				//Переводим из сетевого формата в формат хоста
				U64 nTicks = *((U64 *)tcp_recv_buffer);
				swap((U8 *)&nTicks,(U8 *)&findTicks,sizeof(findTicks));
				currentCmd = P32_CMD_DATA;	
				return TCP_BEFORE_SEND;
			}
			break;
		}
	}
	return TCP_CLOSE;
}

/*
	Call Back 
*/
U16 tcp_callback (U8 soc, U8 event, U8 *ptr, U16 par) 
{
  /* This function is called on TCP event */
    switch (event) {
    case TCP_EVT_CONREQ:
      /* Remote host is trying to connect to our TCP socket. */
      /* 'ptr' points to Remote IP, 'par' holds the remote port. */
       
      /* Return 1 to accept connection, or 0 to reject connection */
      return (1);
    case TCP_EVT_ABORT:
      /* Connection was aborted */
			regOk=0;
			tcpState = TCP_IDLE;
	    break;
    case TCP_EVT_CONNECT:
			return 1; 
      break;
    case TCP_EVT_CLOSE:
      /* Connection has been closed */
       
      break;
    case TCP_EVT_ACK:
      /* Our sent data has been acknowledged by remote peer */
       return 1;
      break;
    case TCP_EVT_DATA:
      /* TCP data frame has been received, 'ptr' points to data */
      /* Data length is 'par' bytes */
			
  		memcpy(tcp_recv_buffer,ptr,par);
			tcp_recv_bufsize=par;
	
			return 1;
      break;
  }
  return (0);
}

void dhcp_check () 
{
  
	if(mem_test (&MY_IP, 0, IP_ADRLEN) == 0) 
	{
		printf("DHCP Ok\r\n");
		needDHCP=0;
	}
	else if (SystemTick-dhcpTime>=5000)
	{
		dhcp_disable ();
		needDHCP=0;
		printf("DHCP failed.Default net parameters\r\n");
	}
	if (!needDHCP)
	{
		printf("IP: %d.%d.%d.%d\r\nNetmask: %d.%d.%d.%d\r\nGateway: %d.%d.%d.%d\r\n", localm[NETIF_ETH].IpAdr[0],localm[NETIF_ETH].IpAdr[1],localm[NETIF_ETH].IpAdr[2],localm[NETIF_ETH].IpAdr[3],localm[NETIF_ETH].NetMask[0],localm[NETIF_ETH].NetMask[1],localm[NETIF_ETH].NetMask[2],localm[NETIF_ETH].NetMask[3],localm[NETIF_ETH].DefGW[0],localm[NETIF_ETH].DefGW[1],localm[NETIF_ETH].DefGW[2],localm[NETIF_ETH].DefGW[3]);
		netParamsOK=1;
	}
}

/*
	tcpServer Call Back 
*/
U16 tcpServer_callback (U8 soc, U8 event, U8 *ptr, U16 par) 
{
  
	if (soc != tcpServer)
    return (0);
  

	/* This function is called on TCP event */
    switch (event) {
    case TCP_EVT_CONREQ:
      /* Remote host is trying to connect to our TCP socket. */
      /* 'ptr' points to Remote IP, 'par' holds the remote port. */
       
      /* Return 1 to accept connection, or 0 to reject connection */
      return (1);
    case TCP_EVT_ABORT:
      /* Connection was aborted */
			
	    break;
    case TCP_EVT_CONNECT:
			return 1; 
      break;
    case TCP_EVT_CLOSE:
      /* Connection has been closed */
       
      break;
    case TCP_EVT_ACK:
      /* Our sent data has been acknowledged by remote peer */
       return 1;
      break;
    case TCP_EVT_DATA:
      /* TCP data frame has been received, 'ptr' points to data */
      /* Data length is 'par' bytes */
			memcpy(tcpServer_recvbuffer,ptr,par);
			tcpServer_recvbufcount=par;
				
			return 1;
      break;
  }
  return (0);
}


U16 udpServer_callback (U8 socket, U8 *remip, U16 port, U8 *buf, U16 len)  
{
  /* This function is called when UDP data has been received. */
	memcpy(udpClient_ip,remip,sizeof(udpClient_ip));
	udpClient_port = port;
	
	memcpy(udpServer_recvbuffer,buf,len);
	udpServer_recvbufcount = len;
  
  return (0);
}

U8 udp_send_data(U8 * data, U16 size, U8 sock)
{
	U8 * send_buf = udp_get_buf (size);
	memcpy(send_buf,data,size);
	udp_send(sock,udpClient_ip,udpClient_port,send_buf,size);
}	

